package com.kexio.auth.aop;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;

/**
 * 日志记录切面
 * 自动记录方法调用日志和参数
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Aspect
@Order(30)
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    /**
     * 记录Service层关键方法调用（仅限于认证相关的服务）
     */
    @Around("execution(* com.kexio..service.*Auth*.*(..))")
    public Object logAuthServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        return logMethodExecution(joinPoint, "AUTH_SERVICE");
    }
    
    /**
     * 记录带@Log注解的方法
     */
    @Around("@annotation(log)")
    public Object logAnnotatedMethods(ProceedingJoinPoint joinPoint, 
                                    com.kexio.auth.annotation.Log log) throws Throwable {
        return logMethodExecution(joinPoint, "ANNOTATED", log);
    }
    
    /**
     * 执行方法日志记录
     */
    private Object logMethodExecution(ProceedingJoinPoint joinPoint, String type) throws Throwable {
        return logMethodExecution(joinPoint, type, null);
    }
    
    /**
     * 执行方法日志记录（带注解参数）
     */
    private Object logMethodExecution(ProceedingJoinPoint joinPoint, String type, 
                                    com.kexio.auth.annotation.Log logAnnotation) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = method.getName();
        Object[] args = joinPoint.getArgs();
        
        // 生成请求ID
        String requestId = generateRequestId();
        
        // 记录方法调用开始
        logMethodStart(type, className, methodName, args, requestId);
        
        long startTime = System.currentTimeMillis();
        Object result = null;
        Throwable exception = null;
        
        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            exception = e;
            throw e;
        } finally {
            long executionTime = System.currentTimeMillis() - startTime;
            
            // 记录方法调用结束
            logMethodEnd(type, className, methodName, result, exception, executionTime, requestId);
        }
    }

    /**
     * 记录方法开始日志
     */
    private void logMethodStart(String type, String className, String methodName, Object[] args, String requestId) {
        try {
            String methodInfo = className + "." + methodName + "()";
            String argsInfo = formatArguments(args);
            if ("CONTROLLER".equals(type)) {
                logger.info("[{}] [{}] 方法调用开始: {} - 参数: {}", type, requestId, methodInfo, argsInfo);
            } else {
                logger.debug("[{}] [{}] 方法调用开始: {} - 参数: {}", type, requestId, methodInfo, argsInfo);
            }
                
        } catch (Exception e) {
            logger.error("记录方法开始日志失败", e);
        }
    }
    
    /**
     * 记录方法结束日志
     */
    private void logMethodEnd(String type, String className, String methodName, Object result, 
                             Throwable exception, long executionTime, String requestId) {
        try {
            String methodInfo = className + "." + methodName + "()";
            
            if (exception != null) {
                if ("CONTROLLER".equals(type)) {
                    logger.error("[{}] [{}] 方法调用失败: {} - 耗时: {}ms - 异常: {}", type, requestId, methodInfo, executionTime, exception.getMessage());
                } else {
                    logger.debug("[{}] [{}] 方法调用失败: {} - 耗时: {}ms - 异常: {}", type, requestId, methodInfo, executionTime, exception.getMessage());
                }
            } else {
                String resultInfo = formatResult(result);
                if ("CONTROLLER".equals(type)) {
                    logger.info("[{}] [{}] 方法调用成功: {} - 耗时: {}ms - 返回: {}", type, requestId, methodInfo, executionTime, resultInfo);
                } else {
                    logger.debug("[{}] [{}] 方法调用成功: {} - 耗时: {}ms - 返回: {}", type, requestId, methodInfo, executionTime, resultInfo);
                }
            }
            
        } catch (Exception e) {
            logger.error("记录方法结束日志失败", e);
        }
    }
    
    /**
     * 格式化方法参数
     */
    private String formatArguments(Object[] args) {
        if (args == null || args.length == 0) {
            return "无参数";
        }
        
        try {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < args.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                
                Object arg = args[i];
                if (arg == null) {
                    sb.append("null");
                } else if (isPrimitiveOrString(arg)) {
                    sb.append(arg.toString());
                } else {
                    // 对于复杂对象，只显示类型
                    sb.append(arg.getClass().getSimpleName()).append("@").append(Integer.toHexString(arg.hashCode()));
                }
            }
            
            // 限制参数日志长度
            String result = sb.toString();
            if (result.length() > 200) {
                result = result.substring(0, 200) + "...";
            }
            
            return result;
            
        } catch (Exception e) {
            return "参数格式化失败: " + e.getMessage();
        }
    }
    
    /**
     * 格式化返回结果
     */
    private String formatResult(Object result) {
        if (result == null) {
            return "null";
        }
        
        try {
            if (isPrimitiveOrString(result)) {
                return result.toString();
            } else {
                // 对于复杂对象，只显示类型和基本信息
                String className = result.getClass().getSimpleName();
                
                // 特殊处理一些常见类型
                if (result instanceof java.util.Collection) {
                    java.util.Collection<?> collection = (java.util.Collection<?>) result;
                    return className + "[size=" + collection.size() + "]";
                } else if (result instanceof java.util.Map) {
                    java.util.Map<?, ?> map = (java.util.Map<?, ?>) result;
                    return className + "[size=" + map.size() + "]";
                } else {
                    return className + "@" + Integer.toHexString(result.hashCode());
                }
            }
            
        } catch (Exception e) {
            return "结果格式化失败: " + e.getMessage();
        }
    }
    
    /**
     * 判断是否为基本类型或字符串
     */
    private boolean isPrimitiveOrString(Object obj) {
        if (obj == null) {
            return true;
        }
        
        Class<?> clazz = obj.getClass();
        return clazz.isPrimitive() || 
               clazz == String.class ||
               clazz == Integer.class ||
               clazz == Long.class ||
               clazz == Double.class ||
               clazz == Float.class ||
               clazz == Boolean.class ||
               clazz == Short.class ||
               clazz == Byte.class ||
               clazz == Character.class;
    }
    
    /**
     * 生成请求ID
     */
    private String generateRequestId() {
        return String.valueOf(System.currentTimeMillis() % 100000);
    }
}
